/**
* @File    :   jwt.go
* @Time    :   2022/05/05
* @Author  :   Mz100
* @Desc    :   jwt token 处理： 生成，删除，验证等处理，结合用户数据
 */
package jwt

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"xdingtec.cn/xdkaika/app"
	"xdingtec.cn/xdkaika/utils/jwt/claims"

	"github.com/golang-jwt/jwt"
)

var jwtSecret []byte

func init() {
	jwtSecret = []byte(app.CONFIG.JWT.SigningKey)
}

// 生成token
func GenerateToken(u *claims.JWTClaims) (string, error) {

	stdClaims := jwt.StandardClaims{
		ExpiresAt: time.Now().Unix() + app.CONFIG.JWT.ExpiresTime,
		Issuer:    app.CONFIG.JWT.Issuer,
	}
	u.StandardClaims = stdClaims

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, u)
	tokenString, err := token.SignedString(jwtSecret)
	if err != nil {
		return "", err
	}
	//redis 存储jwt token
	redis := app.REDIS
	var key = app.CONFIG.Redis.TokenKey + u.OpenId
	err = redis.Set(key, tokenString, time.Duration(app.CONFIG.JWT.ExpiresTime*int64(time.Second)))
	// end redis
	return tokenString, err
}

// 验证token
func ValidateToken(authorization string) (*claims.JWTClaims, error) {
	// 处理 bearer
	if authorization == "" {
		return nil, errors.New("authorization不存在")
	}
	tokenString := strings.TrimSpace(authorization[len("Bearer "):])
	if tokenString == "" {
		return nil, errors.New("令牌不存在")
	}
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	})
	if token == nil || err != nil {
		return nil, errors.New("令牌无效")
	}
	claims := claims.JWTClaims{}
	_, err = jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return jwtSecret, nil
	})
	if err != nil {
		return nil, err
	}
	// redis 验证
	redis := app.REDIS
	var key = app.CONFIG.Redis.TokenKey + claims.OpenId
	redisToken := redis.Get(key)
	if redisToken != nil && redisToken == tokenString {
		return &claims, err
	}
	return nil, err

}

// 删除 token
func DeleteToken(u *claims.JWTClaims) (bool, error) {

	if u.OpenId != "" {
		redis := app.REDIS
		var key = app.CONFIG.Redis.TokenKey + u.OpenId
		err := redis.Delete(key)
		if err == nil {
			return true, nil
		} else {
			return false, err
		}
	}
	return false, errors.New("OpenId 为空")
}
